package com.liuqianfei.markme.rules.inline;

import com.liuqianfei.markme.common.Utils;
import com.liuqianfei.markme.rules.AbstractState;
import com.liuqianfei.markme.rules.IAction;
import com.liuqianfei.util.JSONUtil;
import com.liuqianfei.util.StringUtil;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * Process html entity - &#123;, &#xAF;, &quot;, ...
 * @author liuqianfei
 */
public class EntityAction implements IAction
{
    // ignore case
    String DIGITAL_RE = "^&#((?:x[a-f0-9]{1,8}|[0-9]{1,8}));";
    String NAMED_RE   = "^&([a-z][a-z0-9]{1,31});";

    @Override
    public boolean parse(AbstractState state, boolean silent, int startLine, int endLine)
    {
        char ch;
        int code;
        String[] match;
        int pos = state.getPos();
        int max = state.getPosMax();

        if (state.getSrc().charAt(pos) != 0x26/* & */)
            return false;

        if (pos + 1 < max)
        {
            ch = state.getSrc().charAt(pos + 1);

            if (ch == 0x23 /* # */)
            {
                match = StringUtil.match(DIGITAL_RE, StringUtil.slice(state.getSrc(), pos), true);
                if (match.length > 0)
                {
                    if (!silent)
                    {
                        code = StringUtil.startsWithIgnoreCase(match[1], "x") ? Integer.parseInt(StringUtil.slice(match[1], 1), 16) : Integer.parseInt(match[1], 10);
                        state.setPending(state.getPending() + (Utils.isValidEntityCode(code) ? Utils.fromCodePoint(code) : Utils.fromCodePoint(0xFFFD)));
                    }
                    state.setPos(state.getPos() + match[0].length());
                    return true;
                }
            }
            else
            {
                match = StringUtil.match(NAMED_RE, StringUtil.slice(state.getSrc(), pos), true);
                if (match.length > 0)
                {
                    Map<String, Object> entities = new HashMap<>();
                    try(InputStream inputStream = EntityAction.class.getResourceAsStream("/entities.json"))
                    {
                        byte[] bytes = new byte[inputStream.available()];
                        inputStream.read(bytes);
                        String data = new String(bytes);

                        entities = JSONUtil.jsonToObject(data);
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }

                    if (entities.containsKey(match[1]))
                    {
                        if (!silent)
                            state.setPending(state.getPending() + StringUtil.fromUnicode(entities.get(match[1]).toString()));
                        state.setPos(state.getPos() + match[0].length());
                        return true;
                    }
                }
            }
        }

        if (!silent)
            state.setPending(state.getPending() + "&");

        state.setPos(state.getPos() + 1);
        return true;
    }
}
